#pragma once

//
// helper macros to create static property lists
//
#define MAKEPROP(k, v)		(&CDeviceProperty(k, v))
#define DEVPROPS(...)		__VA_ARGS__, NULL

//
// This class manages a collection of values and hands out
// "IPortableDeviceKeyCollection" and "IPortableDeviceValues"
// objects as needed.
//
class CPortableDeviceProps
{
public:
	//
	// key and value collection objects
	//
	CComPtr<IPortableDeviceKeyCollection>	m_pKeys;
	CComPtr<IPortableDeviceValues>			m_pValues;

	//
	// if initialization fails then this HR stores the error
	// code
	//
	HRESULT									m_hrCreate;

protected:
	HRESULT Init();
	HRESULT InternalAddProps(vector<CDeviceProperty *> props);

public:
	//
	// Constructs a "CPortableDeviceProps" object.
	//
	CPortableDeviceProps();

	//
	// Constructs a "CPortableDeviceProps" object.  Allows convenient
	// syntax to build static properties lists like so:
	//
	//		CPortableDeviceProps props(DEVPROPS(
	//			MAKEPROP( SENSOR_PROPERTY_CONNECTION_TYPE, SENSOR_CONNECTION_TYPE_PC_ATTACHED ),
	//			MAKEPROP( SENSOR_PROPERTY_DESCRIPTION, L"Doofus" )
	//		));
	//
	CPortableDeviceProps(CDeviceProperty *prop, ...);

	//
	// Adds a set of properties to the collection.  Allows convenient
	// syntax to build properties lists like so:
	//
	//		props.AddProps(DEVPROPS(
	//			MAKEPROP( SENSOR_PROPERTY_CONNECTION_TYPE, SENSOR_CONNECTION_TYPE_PC_ATTACHED ),
	//			MAKEPROP( SENSOR_PROPERTY_DESCRIPTION, L"Doofus" )
	//		));
	//
	//	If this method returns a failure code the object must be considered
	//	as being in an inconsistent state.  It will be safer to just call "Clear"
	//	and retry.
	//
	HRESULT AddProps(CDeviceProperty *prop, ...);

	//
	// Sets the value for a given key to an error code.
	//
	HRESULT SetError(REFPROPERTYKEY key, HRESULT code);

	//
	// Clears all keys and values.
	//
	HRESULT Clear();

	//
	// Helper routine to set values for properties in a type independant
	// manner.
	//
	template<typename T>
	HRESULT SetValue(REFPROPERTYKEY key, const T& value)
	{
		//
		// if create failed then we can't do anything
		//
		if(FAILED(m_hrCreate))
			return m_hrCreate;

		//
		// load it up into a "CDeviceProperty" so it maps it to
		// the correct PROPVARIANT object
		//
		CDeviceProperty prop(key, value);

		//
		// if this key already exists then just update value; otherwise
		// add the key
		//
		PROPVARIANT pvar;
		PropVariantInit(&pvar);
		HRESULT hr = m_pValues->GetValue(key, &pvar);
		if(hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
			hr = m_pKeys->Add(key);
		if(FAILED(hr))
			return hr;

		return m_pValues->SetValue(key, &prop.m_value);
	}

	//
	// Helper routine to initialize a property with a PROPVARIANT.
	//
	HRESULT SetValue(REFPROPERTYKEY key, const PROPVARIANT& value);

	//
	// Helper routine to get values for properties in a type independant
	// manner.
	///
	template<typename T>
	HRESULT GetValue(REFPROPERTYKEY key, T& val);
};
